import axios from 'axios'
import { ElMessage } from 'element-plus'

// 默认分片大小20MB
const DEFAULT_CHUNK_SIZE = 20 * 1024 * 1024

/**
 * 大文件分片上传类
 */
class ChunkUploader {
  /**
   * 构造函数
   * @param {Object} options 配置选项
   * @param {File} options.file 要上传的文件
   * @param {number} options.chunkSize 分片大小，默认5MB
   * @param {string} options.uploadUrl 上传地址
   * @param {Function} options.onProgress 进度回调函数
   * @param {Function} options.onSuccess 成功回调函数
   * @param {Function} options.onError 错误回调函数
   * @param {Object} options.extraData 额外数据，如是否为视频等
   */
  constructor(options) {
    this.file = options.file
    this.chunkSize = options.chunkSize || DEFAULT_CHUNK_SIZE
    this.uploadUrl = options.uploadUrl || '/api/file/uploadChunk'
    this.mergeUrl = options.mergeUrl || '/api/file/mergeChunks'
    this.checkUrl = options.checkUrl || '/api/file/checkChunk'
    this.onProgress = options.onProgress || (() => {})
    this.onSuccess = options.onSuccess || (() => {})
    this.onError = options.onError || (() => {})
    this.extraData = options.extraData || {} // 额外数据，如是否为视频等
    
    this.chunks = this.createChunks()
    this.totalChunks = this.chunks.length
    this.uploadedChunks = 0
    this.uploadingChunks = 0
    this.maxConcurrentUploads = 5 // 最大并发上传数量
    this.isUploading = false
    this.fileHash = null
    this.aborted = false
  }
  
  /**
   * 将文件分割成块
   * @returns {Array} 文件块数组
   */
  createChunks() {
    const chunks = []
    let start = 0
    let end = 0
    
    while (start < this.file.size) {
      end = Math.min(start + this.chunkSize, this.file.size)
      chunks.push({
        blob: this.file.slice(start, end),
        start,
        end,
        progress: 0,
        uploaded: false,
        uploading: false,
        retries: 0
      })
      start = end
    }
    
    return chunks
  }
  
  /**
   * 计算文件的MD5哈希值，用于唯一标识
   * 这里使用简单的文件名+文件大小+最后修改时间作为替代
   * 在生产环境应该使用更好的哈希算法
   * @returns {Promise<string>} 文件哈希值
   */
  async computeFileHash() {
    return new Promise((resolve) => {
      const { name, size, lastModified } = this.file
      const hash = `${name}_${size}_${lastModified}`
      resolve(hash)
    })
  }
  
  /**
   * 开始上传
   */
  async start() {
    if (this.isUploading) {
      return
    }
    
    this.isUploading = true
    this.aborted = false
    
    try {
      // 计算文件哈希
      this.fileHash = await this.computeFileHash()
      
      // 上传所有分片
      this.uploadChunks()
    } catch (error) {
      console.error('启动上传失败:', error)
      this.onError(error)
    }
  }
  
  /**
   * 上传文件分片
   */
  uploadChunks() {
    // 检查是否有已上传的分片
    // 这里可以发送请求到服务器检查哪些分片已经上传
    // 为了简单，这里直接上传所有分片
    
    for (let i = 0; i < Math.min(this.maxConcurrentUploads, this.chunks.length); i++) {
      this.uploadNextChunk()
    }
  }
  
  /**
   * 上传下一个分片
   */
  uploadNextChunk() {
    if (this.aborted) {
      return
    }
    
    // 找到下一个未上传且未在上传中的分片
    const chunkIndex = this.chunks.findIndex(chunk => !chunk.uploaded && !chunk.uploading)
    
    if (chunkIndex === -1) {
      // 所有分片都已上传或正在上传
      return
    }
    
    const chunk = this.chunks[chunkIndex]
    chunk.uploading = true
    this.uploadingChunks++
    
    this.uploadChunk(chunk, chunkIndex)
      .then(() => {
        chunk.uploading = false
        chunk.uploaded = true
        this.uploadingChunks--
        this.uploadedChunks++
        
        // 更新总进度
        this.updateProgress()
        
        // 检查是否所有分片都已上传完成
        if (this.uploadedChunks === this.totalChunks) {
          this.mergeChunks()
        } else {
          // 上传下一个分片
          this.uploadNextChunk()
        }
      })
      .catch(error => {
        chunk.uploading = false
        this.uploadingChunks--
        
        // 如果分片上传失败，尝试重试
        if (chunk.retries < 3) {
          chunk.retries++
          console.warn(`分片${chunkIndex}上传失败，正在重试(${chunk.retries}/3)`)
          setTimeout(() => this.uploadNextChunk(), 1000) // 延迟1秒后重试
        } else {
          console.error(`分片${chunkIndex}上传失败，放弃重试`, error)
          this.onError(new Error(`分片${chunkIndex}上传失败，已超过最大重试次数`))
        }
      })
  }
  
  /**
   * 上传单个分片
   * @param {Object} chunk 分片对象
   * @param {number} index 分片索引
   * @returns {Promise} 上传结果
   */
  uploadChunk(chunk, index) {
    return new Promise((resolve, reject) => {
      const formData = new FormData()
      formData.append('file', chunk.blob)
      formData.append('fileHash', this.fileHash)
      formData.append('fileName', this.file.name)
      formData.append('chunkIndex', index)
      formData.append('totalChunks', this.totalChunks)
      
      // 添加额外数据
      Object.keys(this.extraData).forEach(key => {
        formData.append(key, this.extraData[key])
      })
      
      // 获取token
      const token = localStorage.getItem('token')
      
      axios({
        url: this.uploadUrl,
        method: 'post',
        data: formData,
        headers: {
          'Content-Type': 'multipart/form-data',
          'Authorization': token || ''
        },
        onUploadProgress: (progressEvent) => {
          if (progressEvent.lengthComputable) {
            // 更新当前分片的进度
            const percentComplete = (progressEvent.loaded / progressEvent.total) * 100
            chunk.progress = percentComplete
            this.updateProgress()
          }
        }
      })
      .then(response => {
        const res = response.data
        if (res.code !== 1) {
          reject(new Error(res.msg || '上传分片失败'))
          return
        }
        
        chunk.progress = 100
        resolve(res.data)
      })
      .catch(error => {
        console.error(`上传分片${index}失败:`, error)
        reject(error)
      })
    })
  }
  
  /**
   * 更新总体上传进度
   */
  updateProgress() {
    // 计算总进度
    let totalProgress = 0
    this.chunks.forEach(chunk => {
      totalProgress += chunk.progress
    })
    
    const overallProgress = totalProgress / (this.totalChunks * 100)
    this.onProgress(Math.min(Math.round(overallProgress * 100), 99)) // 最多显示到99%，留1%给合并操作
  }
  
  /**
   * 合并所有分片
   */
  mergeChunks() {
    // 获取token
    const token = localStorage.getItem('token')
    
    // 准备请求数据
    const requestData = {
      fileHash: this.fileHash,
      fileName: this.file.name,
      totalChunks: this.totalChunks,
      ...this.extraData // 合并额外数据
    }
    
    axios({
      url: this.mergeUrl,
      method: 'post',
      data: requestData,
      headers: {
        'Content-Type': 'application/json',
        'Authorization': token || ''
      }
    })
    .then(response => {
      const res = response.data
      if (res.code !== 1) {
        this.onError(new Error(res.msg || '合并分片失败'))
        return
      }
      
      this.onProgress(100) // 完成，显示100%
      this.isUploading = false
      this.onSuccess(res.data)
    })
    .catch(error => {
      console.error('合并分片失败:', error)
      this.onError(error)
    })
  }
  
  /**
   * 取消上传
   */
  abort() {
    this.aborted = true
    this.isUploading = false
    
    // 这里可以添加向服务器发送取消请求的逻辑
    // 例如删除已上传的分片
    
    ElMessage.info('上传已取消')
  }
}

/**
 * 创建并启动一个大文件分片上传
 * @param {Object} options 上传选项
 * @returns {ChunkUploader} 上传器实例
 */
export function uploadLargeFile(options) {
  const uploader = new ChunkUploader(options)
  uploader.start()
  return uploader
}

/**
 * 判断是否为大文件需要分片上传
 * @param {File} file 文件对象
 * @param {number} threshold 大小阈值，默认100MB
 * @returns {boolean} 是否为大文件
 */
export function isLargeFile(file, threshold = 100 * 1024 * 1024) {
  return file.size > threshold;
} 